﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using Microsoft.Diagnostics.Tracing;

using Sustainalytics.ESGRatings.BusinessLogic.Computing;
using Sustainalytics.ESGRatings.DataContract.ESG;
using Sustainalytics.ESGComputing.Service.Exceptions;
using Sustainalytics.ESGComputing.Service.Mappers;
using Serilog;

namespace Sustainalytics.ESGComputing.Service.Controllers
{


    /// <summary>
    /// Tools controller.
    /// </summary>
    [RoutePrefix("api/v1/esgcompute")]
    public class ESGComputeController : ApiController
    {
        private readonly ComputationEngine engine;

        public ESGComputeController(ComputationEngine engine)
        {
            this.engine = engine;
        }

        [Route("")]
        public HttpResponseMessage Post([FromBody]ESGRatingsOptionsDto options)
        {
            EventSource.SetCurrentThreadActivityId(Request.GetCorrelationId());
            if (options == null)
            {
                throw new InvalidInput("Invalid computation options");
            }

            // todo:Madalina validate options;
            var computingOptions = options.ToDomainModel();

            {
                Stopwatch watch = new Stopwatch();
                watch.Start();
                //using (var result = this.engine.ComputeESGRatings(computingOptions.SetId, computingOptions.MatrixId, computingOptions.RefUnivId, computingOptions.GroupBy, computingOptions.ComputationCompanyAttributes, computingOptions.Include))
                using (var result = engine.ComputeESGRatings(computingOptions))
                {
                    watch.Stop();
                    Log.Information($"computation elapsed {watch.ElapsedMilliseconds}");


                    ICompanyFilter filter = GetFilter(options, engine);
                    var utils = new ESGComputeMap(result, filter);

                    var dto = new ESGRatingsDto
                    {
                        IndicatorsGroups = utils.ToIndicatorsGroupsDto(),
                        SetId = result.SetId,
                    };

                    if (options.IncludeIndWeightedScores)
                    {
                        dto.IndicatorsCodes = result.Configuration.Codes.ToArray();
                        dto.Indicators = utils.GetIndicators().ToList();
                    }

                    watch.Stop();
                    Log.Information($"buildcontract<Indicators> {watch.ElapsedMilliseconds}");


                    watch.Reset();
                    watch.Start();
                    if (options.IncludeRatings)
                    {
                        dto.Ratings = utils.GetRatings().ToList();
                    }

                    watch.Stop();
                    Log.Information($"buildcontract<Ratings> {watch.ElapsedMilliseconds}");


                    if (options.IncludeAggregatedScores && !options.IncludeTopPerformersInfo)
                    {
                        dto.Groups = utils.GetGroups().ToArray();
                    }

                    if (options.IncludeAggregatedScores && options.IncludeTopPerformersInfo)
                    {
                        dto.Groups = utils.GetGroupsWithTopCompanies().ToArray();
                    }

                    return Request.CreateResponse(HttpStatusCode.OK, dto);
                }
            }


        }

        // todo: should construct a separate factory for getting the filter in order to better test logic
        private static ICompanyFilter GetFilter(ESGRatingsOptionsDto options, ComputationEngine engine)
        {
            if (options.CompaniesIds != null && options.CompaniesIds.Count > 0)
            {
                var additionalCompaniesIds = new List<Guid>();
                if (options.IncludeCompaniesInGroup)
                {
                    foreach (var companyId in options.CompaniesIds)
                    {
                        var companiesToAdd = engine.GetCompaniesInComparativeGroup(
                            companyId,
                            options.MatrixId,
                            options.RefUnivId,
                            options.GroupBy,
                            options.SetId);
                        if (companiesToAdd != null)
                        {
                            additionalCompaniesIds.AddRange(companiesToAdd);
                        }
                    }
                }

                additionalCompaniesIds.AddRange(
                    engine.GetMatchingCompanies(
                        options.MatrixId,
                        options.RefUnivId,
                        options.PeerGroups,
                        options.IndustryGroups, options.SetId));
                return new CompanyFilter(options.CompaniesIds.Union(additionalCompaniesIds));
            }

            if (options.IndustryGroups == null || options.IndustryGroups.Count == 0)
            {
                if ((options.PeerGroups == null) || options.PeerGroups.Count == 0)
                {
                    return null;
                }
            }

            return
                new CompanyFilter(
                    engine.GetMatchingCompanies(
                        options.MatrixId,
                        options.RefUnivId,
                        options.PeerGroups,
                        options.IndustryGroups, options.SetId));
        }
    }
}